package com.sluggard.security.manage.service.impl;

import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sluggard.common.utils.CsvMapper;
import com.sluggard.common.utils.Dates;
import com.sluggard.common.utils.Reflections;
import com.sluggard.security.core.manage.SecurityUserManage;
import com.sluggard.security.manage.mapper.OlogMapper;
import com.sluggard.security.manage.model.entity.OlogEntity;
import com.sluggard.security.manage.model.vo.SecurityUser;
import com.sluggard.security.manage.service.IOlogService;
import com.sluggard.security.properties.SecurityConfigProperties;
import jakarta.annotation.Resource;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 操作日志 服务实现类
 * </p>
 *
 * @author lizheng
 * @since 2024-04-28
 */
@Service
public class OlogServiceImpl extends ServiceImpl<OlogMapper, OlogEntity> implements IOlogService {
    @Resource
    private SecurityConfigProperties securityProperties;

    @Override
    @Scheduled(cron = "${scheduled.olog.cron:0 0 0 * * ?}")
    public void archive() {
        Date executeTime = new Date();
        Date beforeDate = new Date(executeTime.getTime() - securityProperties.getOlog().getDays().toMillis());

        PrintWriter out = null;
        try {
            File dest = getArchiveFile();
            if (dest.exists()) {
                dest.delete();
            }
            out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dest, true), StandardCharsets.UTF_8)));
            Page<OlogEntity> page = Page.of(1, securityProperties.getOlog().getSize());
            QueryWrapper<OlogEntity> ologEntityQueryWrapper = new QueryWrapper<>();
            ologEntityQueryWrapper.lt("operate_time", beforeDate);
            baseMapper.selectPage(page, ologEntityQueryWrapper);

            archivePage(page.getRecords(), out);

            long totalPages = page.getTotal();
            for (int i = 2; i <= totalPages; i++) {
                page = Page.of(i, securityProperties.getOlog().getSize());
                baseMapper.selectPage(page, ologEntityQueryWrapper);
                archivePage(page.getRecords(), out);
            }
            // 先关闭归档文件，然后在归档压缩
            page.setRecords(null);

            IOUtils.closeQuietly(out);
            // 压缩
            try {
                ZipUtil.zip(dest);
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 清理
            cleanup(beforeDate);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(out);
        }
    }

    private File getArchiveFile() {
        return new File(securityProperties.getOlog().getStorage(), Dates.format(Dates.DATETIME_NOT_SEPARATOR) + ".log");
    }

    private void archivePage(List<OlogEntity> ologs, PrintWriter out) {
        for (OlogEntity olog : ologs) {
            out.println(marshal(olog));
        }
        out.flush();
    }

    protected String marshal(OlogEntity olog) {
        return CsvMapper.marshal(olog, Reflections.getSimpleFieldNames(OlogEntity.class).toArray(new String[]{}));
    }

    @Override
    public void cleanup(Date beforeDate) {
        baseMapper.cleanup(beforeDate);
    }

    @Override
    public void log(String moduleName, String actionName, String operateMessage) {
        OlogEntity o = new OlogEntity();
        o.setModuleName(moduleName);
        o.setActionName(actionName);
        // 操作员获取和设置
        try {
            SecurityUser userInfo = SecurityUserManage.getUserInfo();
            o.setOperateUser(userInfo.getUsername() + "[" + userInfo.getRealName() + "]");
            o.setUid(userInfo.getId());
        } catch (Exception ignored) {
        }
        o.setOperateMessage(operateMessage);
        save(o);
    }

    @Override
    public void log(String operateMessage) {
        log("系统功能", "系统日志", operateMessage);
    }

    @Override
    public void log(Exception e) {
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw, true));
        log(sw.toString());
    }

}
